qiankun微前端项目手动挂载多实例场景项目实践(vue2) 您所在的位置:网站首页 PSP游戏转PS3用PKG软件 PSPtoPS3-b22 qiankun微前端项目手动挂载多实例场景项目实践(vue2)

qiankun微前端项目手动挂载多实例场景项目实践(vue2)

2023-03-21 05:58| 来源: 网络整理| 查看: 265

项目背景

公司平台项目是基于qiankun的微前端应用。主、微应用皆是vue2+webpack4。采用基于路由配置挂载子应用的方式。这种方式的优点在于自动激活微应用。当路由url变化时,会和activeRule规则进行匹配,若匹配成功则当前应用会被激活,

产品需求

用户反馈:菜单栏太多,我需要同时看A微应用下的a菜单和B微应用下的b菜单,反复切来切去非常痛苦。

需求内容:在已有导航栏菜单的基础上,再加一个标签导航栏,要支持手动移除。

image.png

需求方案:这个需求本身比较简单,store中定义一个数组,在router.beforeEach中新增元素,在点击移除按钮时删除元素。这里有一个特殊的应用场景,list跳转到detail、add、update等编辑状态的路由时,需要更新标签的路由path属性值,下次点击标签时,依然停留在上次编辑画面,所以没有在点击导航栏菜单的时候新增数组元素。

需求二次迭代

用户反馈:当新增一个产品时,由于创建流程比较长,在创建的过程中,还要去其他页面查看相关数据,再切回来之后,发现刚刚录入的数据都丢了。

需求内容:用户切换标签后,需要保持之前编辑的数据。

需求方案:子应用需要做缓存。vue提供了keep-alive,include prop定义为正则表达式,在需要缓存的组件里定义name属性,其值能匹配上该正则即可。

需求三次迭代

用户反馈:大部分场景都没问题,但是a菜单和b菜单来回切换时,数据要丢失。

问题分析:业务大部分的场景是在同一个微应用下的菜单来回切换,而这里的a菜单和b菜单分别在2个不同的微应用下面。目前采用的是activeRule自动匹配路由激活微应用的方式,它最大的问题是单实例场景。匹配上B微应用后,会先卸载A微应用,再激活B微应用,下次再切到a菜单时,A微应用重新挂载,缓存肯定已失效。

技术本质是qiankun挂载多实例场景。

qiankun多实例实践方案

首先,查阅qiankun官网,微应用配置参数与路由匹配方式是一模一样的。通过手动调用loadMicroApp(app, configuration?)的方式挂载微应用。这个方法返回MicroApp微应用实例,通过调用MicroApp.unmount()手动卸载微应用。

实践开始后,(开发初期,也去各个社区搜索了很多文章,要么内容不符,要么内容太浅,要么没给出具体可落地的方案,但也收获了一些有价值的内容)发现第一个问题,原来是单实例场景,一个dom容器来接收微应用。现在要实现多实例场景,就需要多个dom容器来接收。

step1: 子应用配置放在一个microApps.json文件中,好处在于方便维护,一个微应用有三个重要的属性,name - 名称,port - 端口号(本地开发时的端口号),activeRule - 激活规则。

// microApps.json [ { "name": "microapp1", "port": "3001", "activeRule": "/microapp1/" }, { "name": "microapp2", "port": "3002", "activeRule": "/microapp2/" } ] 复制代码

step2: 多个dom容器接收对应的微应用。这里也体现出配置文件的意义。

优势在于简单粗暴开发效率高。不足之处在于如果同时激活的微实例数量太多,缓存组件也多,缓存组件还特别大,浏览器资源占用过大,导致页面卡顿。目前项目上3个实例同时运行,同时缓存几个体积大点的页面的情况下,页面操作依然很流畅,所以暂不考虑这个问题。

// Layout.vue import microAppsCfg from '@/config/microApps' 复制代码

step3: 手动挂载微应用。

把方法封装在一个loadMicroApp.js中,方便维护。

// loadMicroApp.js import microAppsCfg from '../config/microApps' import { loadMicroApp, addGlobalUncaughtErrorHandler } from 'qiankun' // 其他依赖。。。 const apps = microAppsCfg.map(app => ({ ...app, entry: getEntry(app), // 把port转成微应用地址 props: { baseUrl: app.activeRule, ...initialState }, // 传递给微应用的数据 container: `.app_${app.name}--container` })) // 已激活的实例 const activeApps = {} // 挂载app的方法 const mountMicroApp = path => { const app = apps.find(item => path.startsWith(item.activeRule)) if (app) { const instance = activeApps[app.activeRule] if (instance) { instance.update() } else { activeApps[app.activeRule] = loadMicroApp(app) // 手动加载子应用 } } } // 卸载app的方法 const unmountMicroApps = multipleTabsList => { for (const key in activeApps) { const isExist = multipleTabsList.some( tab => tab.url && tab.url.startsWith(key) ) if (!isExist) { activeApps[key].unmount() // 手动卸载子应用 delete activeApps[key] } } } export { mountMicroApp, unmountMicroApps } 复制代码

这里维护了activeApps - 已激活的实例集合,mountMicroApp - 挂载app的方法,unmountMicroApps - 卸载app的方法。

step4: 踩坑点-切换菜单后,子应用不更新。

根据官网描述,微应用需要多导出一个update钩子。多次尝试后,这样写就没问题

export async function mount(props) { render(props) } // 新增:导出一个空的 update 钩子即可 export async function update() {} export async function unmount() { vueInst.$destroy() vueInst.$el.innerHTML = '' vueInst = null router = null } 复制代码

再回到mountMicroApp方法里,第六行关键代码 instance.update(),子应用路由终于正常更新了。

step5: 挂载微应用的时机。

放在路由跳转前钩子里比较合适。

// router/index.js router.forEach((to, form, next) => { mountMicroApp(to.path) }) 复制代码

step6: 卸载微应用的时机。

当维护的标签数组发生变化时,调用卸载微应用的方法比较合适。acitons.js维护的是qiankun通信的action实例。这是一个典型的发布订阅模式。通过initGlobalState生成一个action实例,当updateMultipleTabsList标签数组发生变化时,执行订阅的回调函数,此时调用unmountMicroApps卸载方法。

// actions.js import { initGlobalState } from 'qiankun' import { unmountMicroApps } from '@/core/loadMicroApp' export const initialState = { themeColor: '#003993', // 主题色 baseWindow: window, updateMultipleTabsList: [] // 多页签更新列表,子应用监听处理删除keep-alive缓存 } const actions = initGlobalState(initialState) actions.onGlobalStateChange(state => { unmountMicroApps(state.updateMultipleTabsList) }) export default actions 复制代码

这里在主应用的store里维护了updateMultipleTabsList数组,同时在通信的GlobalState里也维护着同一个updateMultipleTabsList数组。

step6: 微应用keep-alive缓存的生命周期。

首先,当标签栏发生变化时(新增、删除、修改),利用qiankun提供的通信方式通知各个子应用。

actions.setGlobalState({ updateMultipleTabsList: multipleTabsList }) 复制代码

最后剩下如何手动清除微应用keep-alive中缓存的菜单组件。网上资料也挺多的。这里我的视线思路大概如下: router监听判断是否是缓存菜单:

curNode.$vnode.data.keepAlive 复制代码

获取keep-alive缓存列表数据:

// {[cid]: componentInstance} const cache = curNode.parent.componentInstance.cache // [cid] const keys = curNode.parent.componentInstance.keys 复制代码

获取当前组件的cid:

const cid = curNode.componentOptions.Ctor.cid 复制代码

删除组件缓存:

delete cache[cid] keys.splice(keys.indexOf(cid), 1) curNode.$destroy() 复制代码 结语

以上就是qiankun微前端架构下的页签缓存功能的踩坑史,希望对大家有所帮助。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有